package com.gzc.just.play.last.war.common.router;

import com.gzc.just.play.last.war.common.connection.MessageDecoder;
import com.gzc.just.play.last.war.common.connection.ServerConnectionManager;
import com.gzc.just.play.last.war.common.handler.ServerSession;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Message router
 * Implements server-to-server message routing and forwarding, supporting normal routing and dynamic routing during battles
 */
@Component
public class MessageRouter {
    
    private static final Logger log = LoggerFactory.getLogger(MessageRouter.class);
    
    private final ServerConnectionManager connectionManager;
    
    public MessageRouter(ServerConnectionManager connectionManager) {
        this.connectionManager = connectionManager;
    }
    
    // Routing table: message ID -> target server type
    private final ConcurrentHashMap<Integer, RouteInfo> routingTable = new ConcurrentHashMap<>();
    
    // User-server mapping (used during battles)
    private final ConcurrentHashMap<Long, ServerSession> userBattleRouting = new ConcurrentHashMap<>();
    
    // User-scene server mapping (used during battles)
    private final ConcurrentHashMap<Long, ServerConnectionManager.ServerType> userSceneRouting = new ConcurrentHashMap<>();
    
    /**
     * Route information
     */
    public static class RouteInfo {
        private final ServerConnectionManager.ServerType targetServer;
        private final boolean requireDirectRouting; // 是否需要直接路由
        
        public RouteInfo(ServerConnectionManager.ServerType targetServer, boolean requireDirectRouting) {
            this.targetServer = targetServer;
            this.requireDirectRouting = requireDirectRouting;
        }
        
        public ServerConnectionManager.ServerType getTargetServer() {
            return targetServer;
        }
        
        public boolean isRequireDirectRouting() {
            return requireDirectRouting;
        }
    }
    
    /**
     * Initialize routing table
     */
    public void initializeRoutingTable() {
        // Login messages: GateServer -> LoginServer
        routingTable.put(1001, new RouteInfo(ServerConnectionManager.ServerType.LOGIN_SERVER, false));
        
        // User info query: GateServer -> CentralServer
        routingTable.put(2001, new RouteInfo(ServerConnectionManager.ServerType.CENTRAL_SERVER, false));
        
        // Battle messages: GateServer -> SceneServer (direct routing)
        routingTable.put(3001, new RouteInfo(ServerConnectionManager.ServerType.SCENE_SERVER, true));
        routingTable.put(3002, new RouteInfo(ServerConnectionManager.ServerType.SCENE_SERVER, true));
        routingTable.put(3003, new RouteInfo(ServerConnectionManager.ServerType.SCENE_SERVER, true));
        
        // Matching messages: CentralServer -> SceneServer
        routingTable.put(4001, new RouteInfo(ServerConnectionManager.ServerType.SCENE_SERVER, false));
        
        // Guild messages: GateServer -> CentralServer
        routingTable.put(5001, new RouteInfo(ServerConnectionManager.ServerType.CENTRAL_SERVER, false));
        
        log.info("Initialized routing table with {} routes", routingTable.size());
    }
    
    /**
     * Route message
     * @param messageId Message ID
     * @param data Message data
     * @param sourceServer Source server
     * @param userId User ID (optional)
     * @return Whether routing was successful
     */
    public boolean routeMessage(int messageId, byte[] data, ServerConnectionManager.ServerType sourceServer, Long userId) {
        RouteInfo routeInfo = routingTable.get(messageId);
        
        if (routeInfo == null) {
            log.warn("No routing information found for message ID: {}", messageId);
            return false;
        }
        
        ServerConnectionManager.ServerType targetServer = routeInfo.getTargetServer();
        
        // Check if direct routing to user's scene server is needed (during battles)
        if (routeInfo.isRequireDirectRouting() && userId != null && userSceneRouting.containsKey(userId)) {
            targetServer = userSceneRouting.get(userId);
            log.debug("Using direct routing for user {} to scene server", userId);
        }
        
        // If target server is the source server, no routing needed
        if (targetServer == sourceServer) {
            log.debug("Message {} is already at target server {}", messageId, targetServer.getName());
            return true;
        }
        
        // Get connection and forward message
        Channel channel = connectionManager.getConnection(targetServer);
        if (channel == null || !channel.isActive()) {
            log.error("No active connection to server {} for message {}", targetServer.getName(), messageId);
            return false;
        }
        
        // Construct message and send
        MessageDecoder.ServerMessage message = new MessageDecoder.ServerMessage(messageId, data);
        channel.writeAndFlush(message);
        
        log.debug("Routed message {} from {} to {}", messageId, sourceServer.getName(), targetServer.getName());
        return true;
    }
    
    /**
     * Add user battle routing mapping
     * @param userId User ID
     * @param sceneSession Scene server session
     */
    public void addUserBattleRouting(Long userId, ServerSession sceneSession) {
        userBattleRouting.put(userId, sceneSession);
        // Find the corresponding scene server type
        // Here it's assumed that the server type can be obtained from the session
        // Actual implementation may need more information to determine
        log.debug("Added battle routing for user {} to scene server", userId);
    }
    
    /**
     * Add user scene server mapping
     * @param userId User ID
     * @param sceneServerType Scene server type
     */
    public void addUserSceneRouting(Long userId, ServerConnectionManager.ServerType sceneServerType) {
        userSceneRouting.put(userId, sceneServerType);
        log.debug("Added scene routing for user {} to scene server {}", userId, sceneServerType.getName());
    }
    
    /**
     * Remove user battle routing mapping
     * @param userId User ID
     */
    public void removeUserBattleRouting(Long userId) {
        userBattleRouting.remove(userId);
        userSceneRouting.remove(userId);
        log.debug("Removed battle routing for user {}", userId);
    }
    
    /**
     * Forward message to user's scene server
     * @param userId User ID
     * @param messageId Message ID
     * @param data Message data
     * @return Whether forwarding was successful
     */
    public boolean forwardToSceneServer(Long userId, int messageId, byte[] data) {
        ServerConnectionManager.ServerType sceneServerType = userSceneRouting.get(userId);
        if (sceneServerType == null) {
            log.warn("No scene server routing found for user {}", userId);
            return false;
        }
        
        Channel channel = connectionManager.getConnection(sceneServerType);
        if (channel == null || !channel.isActive()) {
            log.error("No active connection to scene server {} for user {}", 
                      sceneServerType.getName(), userId);
            return false;
        }
        
        // Construct message and send
        MessageDecoder.ServerMessage message = new MessageDecoder.ServerMessage(messageId, data);
        channel.writeAndFlush(message);
        
        log.debug("Forwarded message {} to scene server {} for user {}", 
                 messageId, sceneServerType.getName(), userId);
        return true;
    }
    
    /**
     * Get routing statistics
     * @return Routing statistics information
     */
    public Map<String, Object> getRoutingStatistics() {
        Map<String, Object> stats = new ConcurrentHashMap<>();
        stats.put("routingTableSize", routingTable.size());
        stats.put("activeBattleRoutes", userBattleRouting.size());
        stats.put("activeSceneRoutes", userSceneRouting.size());
        
        Map<String, Integer> serverCounts = new ConcurrentHashMap<>();
        routingTable.values().forEach(routeInfo -> {
            String serverName = routeInfo.getTargetServer().getName();
            serverCounts.merge(serverName, 1, Integer::sum);
        });
        stats.put("serverMessageCounts", serverCounts);
        
        return stats;
    }
    
    /**
     * Clean up invalid route mappings
     */
    public void cleanupInvalidRoutes() {
        // Clean up routes without corresponding scene servers
        userSceneRouting.entrySet().removeIf(entry -> {
            ServerConnectionManager.ServerType serverType = entry.getValue();
            boolean isConnected = connectionManager.isConnected(serverType);
            if (!isConnected) {
                log.info("Removing invalid scene routing for user {} to server {}", 
                         entry.getKey(), serverType.getName());
                return true;
            }
            return false;
        });
        
        // Clean up battle routes without corresponding sessions
        userBattleRouting.entrySet().removeIf(entry -> {
            ServerSession session = entry.getValue();
            boolean isActive = session != null && session.isActive();
            if (!isActive) {
                log.info("Removing invalid battle routing for user {}", entry.getKey());
                return true;
            }
            return false;
        });
    }
}